package com.example.ipctest;


import android.annotation.TargetApi;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by josan_tang on 9/8/17.
 */

public class BookManagerService extends Service {
    private static final String TAG = "BMS";
    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
    /**
     * 使用RemoteCallbackList而没有使用CopyOnWriteArrayList，
     * 原因是因为Binder会把客户端传递过来的对象重新转化并生成新的对象
     * 虽然我嘛在客户端传递的是同一对象，但是通过Binder传递给服务端，却生成了两个全新的对象
     * RemoteCallbackList是系统专门提供用于删除跨进程listener的接口
     * 它内部有个key为IBinder类型，value是Callback雷习惯的Map
     * 而客户端传递相同对象时，底层的Binder对象是相同的，也就是说两次传递到服务端的Binder是同一对象
     */
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();
    /**
     * 因为AIDL中定义的目标函数，是运行在服务端的Binder线程池中的，所以需要处理线程同步问题
     */
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {

        /**
         * 这里是运行在服务端中的Binder线程池中的，所以不能直接去更新UI，但也不需要新开线程进行异步操作
         * 这个方法是在服务端的IBookManager.Stub的onTransact方法中调用的
         * @param i
         * @return
         * @throws RemoteException
         */
        @Override
        public List<Book> getBookList(int i) throws RemoteException {
            Log.e(TAG, "getBookList: ");
            /*
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            if (mBookList != null) {
                int size = mBookList.size();
                Log.e(TAG, "bookList size = " + size);
                for (int j = 0; j < size; j++) {
                    Log.e(TAG, "bookName=" + mBookList.get(j).name);
                }
            }
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public void registerLisenter(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.register(listener);
            Log.e(TAG, "registerLisenter,size:" + mListenerList.getRegisteredCallbackCount());
        }

        @Override
        public void unRegisterLisenter(IOnNewBookArrivedListener listener) throws RemoteException {
            mListenerList.unregister(listener);
            Log.e(TAG, "unRegisterLisenter,size:" + mListenerList.beginBroadcast());
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind: ");
        return mBinder;
    }

    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate: ");
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS"));

        new Thread(new ServiceWorker()).start();

    }

    /**
     * 新书通知
     * @param book
     * @throws RemoteException
     */
    private void onNewBookArrived(final Book book) throws RemoteException {
        mBookList.add(book);
        new Thread(new Runnable() {
            @Override
            public void run() {
                final int N = mListenerList.beginBroadcast();
                Log.e(TAG, "onNewBookArrived,notify listeners:" + N);
                Log.e(TAG, "onNewBookArrived: thread :" + Thread.currentThread().getName());
                for (int i = 0; i < N; i++) {
                    IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
                    if (listener != null) {
                        try {
                            /**
                             * listener.onNewBookArrived方法被回调，是在客户端的Binder线程池中
                             * 这里跟在客户端在onServiceConnected方法中获取到服务端AIDL接口后，调用服务端方法的逻辑是一致的
                             */
                            listener.onNewBookArrived(book);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }
                mListenerList.finishBroadcast();
            }
        }).start();
    }
    /**
     * 服务端任务
     */
    private class ServiceWorker implements Runnable {

        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "newBook#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
